package edu.snake.implement.model.character.movement;


import edu.snake.core.model.arena.Arena;
import edu.snake.core.model.arena.Position;
import edu.snake.core.model.character.Character;
import edu.snake.core.model.character.CharacterState;
import edu.snake.core.model.character.Movement;
import edu.snake.core.service.LiveOrDieService;
import edu.snake.implement.model.arena.Direction;
import edu.snake.implement.utils.PairCharacter;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;

public abstract class AbstractMoveSimple implements Movement {

    protected Character character;
    protected Arena arena;
    protected LiveOrDieService liveOrDieService;

    public AbstractMoveSimple(Character character, Arena arena, LiveOrDieService liveOrDieService) {
        if (liveOrDieService == null) {
            throw new NullPointerException("Cannot construct class, liveOrDieService is null ");
        }
        this.character = character;
        this.arena = arena;
        this.liveOrDieService = liveOrDieService;
    }


    @Override
    public final Character performMovement() {
        if (!character.isAlive()) {
            return null;
        }

        Lock lock = arena.getLock();
        lock.lock();
        Position currentPosition = arena.get(character);
        Character characterFromArena;
        try {
            List<Direction> directionList = lookingArena(this.arena, character);
            Direction direction = calculateNewDirection(directionList);
            if (direction == null) {
                characterFromArena = null;
            } else {
                characterFromArena = arena.move(direction, character);
                if (characterFromArena != null) {
                    liveOrDead(characterFromArena);
                }

            }
            if (character.getState() == CharacterState.WITH_ELEMENT) {
                Iterator<Character> iterator = character.iterator();
                while (iterator.hasNext()) {
                    Character character1 = iterator.next();
                    Position position = arena.get(character1);
                    Position positionNew = currentPosition;
                    currentPosition = position;
                    if (!character.isAlive()) {
                        break;
                    }
                    arena.move(positionNew, character1);
                }
            }
        } finally {
            lock.unlock();
        }
        return characterFromArena;
    }

    protected final void liveOrDead(Character characterFromArena) {
        liveOrDieService.calculateLiveOrDie(new PairCharacter(this.character, characterFromArena));
    }

    protected abstract List<Direction> lookingArena(Arena arena, Character character);

    protected abstract Direction calculateNewDirection(List<Direction> positionList);
}
